package com.zzl.study.auth.config;

import com.zzl.study.auth.security.authentication.client.GatewayOAuth2AuthorizedClient;
import com.zzl.study.auth.security.handler.GatewayLogoutHandler;
import com.zzl.study.auth.security.handler.GatewayLogoutSuccessHandler;
import com.zzl.study.auth.security.repository.RedisServerSecurityContextRepository;
import com.zzl.study.auth.security.service.RedisReactiveOAuth2AuthorizedClientService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @author: zhangzl
 * @date: 2024/3/13 11:11
 * @version: 1.0
 * @description: 统一认证授权中心客户端安全配置类
 */
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class AuthorizationClientSecurityConfig {

    /**
     * 解析用户权限信息（当在浏览器中直接访问接口，框架自动调用OIDC流程登录时会用到该配置）
     *
     * @return GrantedAuthoritiesMapper
     */
    @Bean
    public GrantedAuthoritiesMapper userAuthoritiesMapper() {
        return (authorities) -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
            authorities.forEach(authority -> {
                if (authority instanceof OAuth2UserAuthority oAuth2UserAuthority) {
                    // 从认证服务获取的用户信息中提取权限信息
                    Object userAuthorities = oAuth2UserAuthority.getAttributes().get("authorities");
                    if (userAuthorities instanceof Collection<?> collection) {
                        // 转为SimpleGrantedAuthority的实例并插入mappedAuthorities中
                        collection.stream().filter(a -> a instanceof String)
                                .map(String::valueOf)
                                .map(SimpleGrantedAuthority::new)
                                .forEach(mappedAuthorities::add);
                    }
                }
            });
            return mappedAuthorities;
        };
    }

    /**
     * 配置认证相关的过滤器链
     *
     * @param http Spring Security的核心配置类
     * @return 过滤器链
     */
    @Bean
    public SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http, GatewayLogoutHandler logoutHandler) {
        // 禁用csrf与cors
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);
        http.cors(ServerHttpSecurity.CorsSpec::disable);
        http.securityContextRepository(redisSecurityContextRepository());
        // 开启全局验证
        http.authorizeExchange((authorize) -> authorize
                .pathMatchers("/login-ui").permitAll()
                //全部需要认证
                .anyExchange().authenticated()
        );
        // 开启OAuth2登录
        http.oauth2Login(Customizer.withDefaults());
        // 退出登录
        http.logout((logout) -> logout
                .logoutHandler(logoutHandler)
                .logoutSuccessHandler(gatewayLogoutSuccessHandler())
        );
        // 设置当前服务为资源服务，解析请求头中的token
        http.oauth2ResourceServer((resourceServer) -> resourceServer
                        // 使用jwt
                        .jwt(Customizer.withDefaults())
                /*
                // xhr请求未携带Token处理
                .authenticationEntryPoint(this::authenticationEntryPoint)
                // 权限不足处理
                .accessDeniedHandler(this::accessDeniedHandler)
                // Token解析失败处理
                .authenticationFailureHandler(this::failureHandler)
                */
        );
        return http.build();
    }

    /**
     * 向Spring容器中注入一个基于Redis存储的SecurityContextRepository
     *
     * @return 基于Redis存储的SecurityContextRepository
     */
    @Bean
    public ServerSecurityContextRepository redisSecurityContextRepository() {
        return new RedisServerSecurityContextRepository();
    }

    /**
     * 向Spring容器中注入一个基于Redis的授权客户端服务
     *
     * @param clientRegistrationRepository        客户端注册信息存储器
     * @param oauth2AuthorizedClientRedisTemplate 存储OAuth2认证客户端的RedisTemplate
     * @return 基于Redis的授权客户端服务
     */
    @Bean
    public ReactiveOAuth2AuthorizedClientService redisReactiveOAuth2AuthorizedClientService(ReactiveClientRegistrationRepository clientRegistrationRepository,
                                                                                            ReactiveRedisTemplate<String, GatewayOAuth2AuthorizedClient> oauth2AuthorizedClientRedisTemplate) {
        return new RedisReactiveOAuth2AuthorizedClientService(clientRegistrationRepository, oauth2AuthorizedClientRedisTemplate);
    }

    @Bean
    public GatewayLogoutHandler gatewayLogoutHandler(ReactiveOAuth2AuthorizedClientService reactiveOAuth2AuthorizedClientService, ServerSecurityContextRepository serverSecurityContextRepository) {
        return new GatewayLogoutHandler(reactiveOAuth2AuthorizedClientService, serverSecurityContextRepository);
    }

    @Bean
    public GatewayLogoutSuccessHandler gatewayLogoutSuccessHandler() {
        return new GatewayLogoutSuccessHandler();
    }

}
